package db;

import java.util.List;

import org.hibernate.FlushMode;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import formulation.BeanFactory;

import java.lang.reflect.ParameterizedType;

public class AbstractService<TObject> implements CommonDAO<TObject>
{
    protected SessionFactory sessionFactory;
    
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    
    @SuppressWarnings("unchecked")
    protected Class<TObject> getRealClass() {
        return (Class<TObject>)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }
    
    protected String getClassName() {
        String longName = getRealClass().toString();
        return longName.substring(longName.lastIndexOf('.') + 1);
    }
    
    public TObject insert(TObject obj) {
        Session session = sessionFactory.getCurrentSession();
        session.setFlushMode(FlushMode.AUTO);
        Transaction tx = session.getTransaction();
        try {
            tx.begin();
            session.saveOrUpdate(obj);
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            //session.close();
        }
        return obj;
    }

    @SuppressWarnings("unchecked")
    public TObject remove(Long objId) {
        Session session = sessionFactory.getCurrentSession();
        session.setFlushMode(FlushMode.AUTO);
        Transaction tx = session.getTransaction();
        TObject obj = null;
        try {
            tx.begin();
            obj = (TObject)session.get(getRealClass(), objId);
            session.delete(obj);
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            //session.close();
        }
        return obj;
    }

    @SuppressWarnings("unchecked")
    public List<TObject> list() {
        Session session = sessionFactory.getCurrentSession();
        try {
            Query query = session.createQuery("from " + getClassName());
            return query.list();
        } finally {
            //session.close();
        }
    }

    @SuppressWarnings("unchecked")
    public TObject find(Long objId)
    {
        Session session = sessionFactory.getCurrentSession();
        try {
            Query query = session.createQuery("from " + getClassName() + " where id = " + String.valueOf(objId));
            return (TObject)query.uniqueResult();
        } finally {
            //session.close();
        }
    }
    
    public long searchCount(String key) {
        Session session = sessionFactory.getCurrentSession();
        try {
            Query query = session.createQuery("select count(*) from " + getClassName() + " where name like :name");
            query.setString("name", "%" + key + "%");
            return ((Long)query.uniqueResult()).longValue();
        } finally {
            //session.close();
        }
    }
    
    @SuppressWarnings("unchecked")
    public List<TObject> search(String key, int first, int max)
    {
        Session session = sessionFactory.getCurrentSession();
        try {
            Query query = session.createQuery("from " + getClassName() + " where name like :name");
            query.setString("name", "%" + key + "%");
            query.setFirstResult(first);
            query.setMaxResults(max);
            return query.list();
        } finally {
            //session.close();
        }   
    }
    
    public void flush() {
        Session session = sessionFactory.getCurrentSession();
        session.flush();
    }
    
    public void reopenSession() {
        closeSession();
        openSession();
    }
    
    private void openSession() {
        SessionFactory sessionFactory = (SessionFactory) BeanFactory.getWebApplicationContext().getBean("sessionFactory", SessionFactory.class);
        Session session = (Session) SessionFactoryUtils.getSession(sessionFactory, true);
        session.setFlushMode(FlushMode.AUTO);
        TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
    }
    
    private void closeSession() {
        SessionHolder sessionHolder =
            (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
        SessionFactoryUtils.closeSession(sessionHolder.getSession());
    }
}
